/*
 * Project CHEN Rendering Engine : /area.cpp
 * Created: 2020-12-16, Last modified:  2021-01-27
 * 
 * Implementations of functions declared in area.hpp
 * 
 * Copyright (C) 2021 Hineven (hineven@pku.edu.cn)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "area.hpp"
namespace chen {

Spectrum DiffuseAreaLight::sampleLi(
    const Interact & si, const Point2 & u,
    Vector3 * wi, Float * pdf, bool * occ
) const {
    if(si.hit == this->shape) {
        *occ = true;
        return Spectrum(0);
    }
    Interact in;
    if(!hasFlag(this->shape->flags, FSHAPE_SUPUSPFR)) {
        this->shape->uniformSample(u, &in, nullptr, pdf);
        in.flipped = flipped;
    } else {
        this->shape->uniformSampleFrom(u, si, &in, nullptr, pdf);
        if(in.flipped != flipped) {
            *occ = true;
            return 0;
        }
    }
    Vector3 dir = si.p-in.p;
    Float ef = in.ng.weigh(dir);
    if(ef == 0 || ((ef < 0) ^ flipped)) {
        *occ = true;
        return 0;
    }
    Ray cray = si.spawnRayTo(in.getOutside(in.flipped), si.flipped);
    const Shape * tmp = this->strc->testRay(cray);
    if(tmp != nullptr && !(tmp == this->shape && requal(cray.t, 1))) {
        *occ = true;
        return 0;
    }
    *wi = (si.p-in.p).normalized();
    // Trannsform PDF defined over surface area to solid angle
    *occ = false;
    *pdf =  *pdf*dir.lengthSquared()/abs(in.ng.weigh(*wi)); 
    return identity;
}


Spectrum DiffuseAreaLight::sampleLo (
    const Point2 & up, const Point2 & ud, Ray * r, Float * pdf, Float * cosv
    ) const {
    Interact in;
    this->shape->uniformSample(up, &in, nullptr, pdf);
    Float costheta = ud.y;
    Float sintheta = std::sqrt(max((Float)1-ud.y*ud.y, (Float)0));
    Float phi = ud.x*(Float)(2*M_PI);
    if(flipped) costheta = -costheta;
    *r = in.spawnRay(
        in.ng.rcoord(
            (Normal)in.ng.getPerpendicularUnit(),
            Vector3(sin(phi)*sintheta, cos(phi)*sintheta, costheta)
        ),
        flipped
    );
    *pdf = *pdf/((Float)(2*M_PI));
    *cosv = abs(costheta);
    return identity;
}
}